#ifndef _COMPILATION_TASK_H_
#define _COMPILATION_TASK_H_

#include <compilation/Forward.h>

#include <list>
#include <memory>

#include <compilation/task/Traits.h>

#include <compilation/instruction/Instruction.h>
#include <compilation/instruction/InstructionVisitor.h>


namespace compilation
{

class VirtualTask
{
private:

  VirtualInstruction* m_instruction;

protected:

  static Comparison fcompare(int uFst, int uSnd)
  { return uFst < uSnd ? CLess : ((uFst > uSnd) ? CGreater : CEqual); }

public:

  VirtualTask(void)
    : m_instruction(nullptr) {}

  VirtualTask(const VirtualTask& source)
    : m_instruction(source.m_instruction) {}

  virtual Comparison compare(const VirtualTask& source) const
  {
    return fcompare(
          getInstruction().getRegistrationIndex(),
          source.getInstruction().getRegistrationIndex());
  }

  virtual ~VirtualTask(void) {}

  virtual VirtualTask* clone(void) const
  { return new VirtualTask(*this); }

  virtual bool mergeWith(VirtualTask& source)
  { return true; }

  virtual void applyOn(
      VirtualInstruction& instruction,
      VirtualAgenda& continuations) {}

  bool operator ==(const VirtualTask& source) const
  { return compare(source) == CEqual; }

  bool operator <(const VirtualTask& source) const
  { return compare(source) == CLess; }

  bool operator >(const VirtualTask& source) const
  { return compare(source) == CGreater; }

  bool operator <=(const VirtualTask& source) const
  { Comparison cResult = compare(source); return cResult == CLess || cResult == CEqual; }

  bool operator >=(const VirtualTask& source) const
  { Comparison cResult = compare(source); return cResult == CGreater || cResult == CEqual; }

  bool operator !=(const VirtualTask& source) const
  { Comparison cResult = compare(source); return cResult == CGreater || cResult == CLess; }

  bool hasInstruction(void) const
  { return m_instruction; }

  VirtualInstruction& getInstruction(void) const
  {
    assert(m_instruction);
    return *m_instruction;
  }

  VirtualTask& setInstruction(const VirtualInstruction& instruction)
  {
    m_instruction = &const_cast<VirtualInstruction&>(instruction);
    return *this;
  }

  VirtualTask& clearInstruction(void)
  {
    m_instruction = nullptr;
    return *this;
  }

  template <typename _Type>
  _Type const& as(void) const
  {
    assert(dynamic_cast<_Type const*>(this));
    return reinterpret_cast<_Type const&>(*this);
  }

  template <typename _Type>
  _Type& as(void)
  {
    assert(dynamic_cast<_Type const*>(this));
    return reinterpret_cast<_Type&>(*this);
  }
};

class WorkListReusability
{
private:

  bool m_reuse;
  bool m_sorted;

public:

  WorkListReusability(void) : m_reuse(false), m_sorted(false) {}

  WorkListReusability(const WorkListReusability& source)
    : m_reuse(source.m_reuse), m_sorted(source.m_sorted) {}

  bool isReusable(void) const
  { return m_reuse; }

  bool isSorted(void) const
  { return m_sorted; }

  WorkListReusability& setReuse(void)
  {
    m_reuse = true;
    return *this;
  }

  WorkListReusability& setSorted(void)
  {
    m_sorted = true;
    return *this;
  }

  void clear(void)
  {
    m_reuse = false;
    m_sorted = false;
  }

  friend class VirtualAgenda;
};

class VirtualAgenda
{
private:

  std::list<VirtualTask*> m_tasks;

public:

  typedef WorkListReusability Reusability;
  typedef VirtualTask Task;

  VirtualAgenda(void) {}

  virtual ~VirtualAgenda(void)
  {
    for (std::list<VirtualTask*>::iterator iIter = m_tasks.begin();
         iIter != m_tasks.end(); ++iIter)
      if (*iIter) delete (*iIter);
    m_tasks.clear();
  }

  bool isEmpty(void) const
  { return m_tasks.empty(); }

  void addNewAsFirst(VirtualTask* task);

  void addNewAsLast(VirtualTask* task);

  void addNewSorted(VirtualTask* task);

  VirtualTask* extractFirst(void)
  {
    VirtualTask* result = m_tasks.front();
    m_tasks.pop_front();
    return result;
  }

  VirtualTask* extractLast(void)
  {
    VirtualTask* result = m_tasks.back();
    m_tasks.pop_back();
    return result;
  }

  virtual void markInstructionWith(
      VirtualInstruction& instruction,
      VirtualTask& task)
  { instruction.mark = (void*) 1; }
};

} // compilation

// execute function.
namespace compilation
{

template <typename _Agenda>
void execute(_Agenda& ag)
{
  typedef typename _Agenda::Task task;

  while (!ag.isEmpty())
  {
    VirtualTask* first = ag.extractFirst();
    assert(dynamic_cast<task*>(first));
    std::unique_ptr<task> current(reinterpret_cast<task*>(first));
    WorkListReusability reuse;
    ag.handleInstruction(&current->getInstruction(), *current, ag, reuse);

    if (reuse.isReusable())
    {
      DEBUG_INSTRUCTION_VISITOR
          << "reuse task"
          << std::endl;

      if (reuse.isSorted())
        ag.addNewSorted(current.release());
      else
        ag.addNewAsFirst(current.release());
    }
  }
}

} // compilation

#endif // _COMPILATION_TASK_H_
